#ifndef __M_SS_H__
#define __M_SS_H__
#include "util.hpp"
#include <unordered_map>

typedef enum
{
  UNLOGIN,
  LOGIN
} ss_statu;

class session
{
private:
  uint64_t _ssid;            // session的id
  uint64_t _uid;             // session所属的用户的id
  ss_statu _statu;           // 状态-----登录或未登录
  wsserver_t::timer_ptr _tp; // 计时器！！！！！----------如果是空的，那么就定为永久存在！！！

public:
  session(uint64_t ssid) : _ssid(ssid) { DBG_LOG("SESSION %p 被创建！！", this); }
  ~session() { DBG_LOG("SESSION %p 被释放！！", this);; }
  void set_user(uint64_t uid) { _uid = uid; }
  void set_status(ss_statu statu) { _statu = statu; }
  uint64_t get_id() { return _uid; }
  bool is_login() { return (_statu == LOGIN); }
  uint64_t ssid() { return _ssid; }

  void set_timer(const wsserver_t::timer_ptr &tp) // 设置计时器
  {
    _tp = tp;
  }
  wsserver_t ::timer_ptr &get_timer() // 获取计时器！！！
  {
    return _tp;
  }
};

using session_ptr = std::shared_ptr<session>;

#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1

class session_manager
{
private:
  uint64_t next_ssid; // session的id计数器------
  std::mutex _mutex;
  std::unordered_map<uint64_t, session_ptr> _session; // 建立一个哈希表------是session的id和session智能指针做键值对！！！！！
  wsserver_t *_server;                                // 要有服务器对象！！！！
public:
  session_manager(wsserver_t *server) : next_ssid(1), _server(server) { DBG_LOG("初始化session_manager成功\n"); }
  ~session_manager() { DBG_LOG("初始化session_manager成功\n"); }

  session_ptr create_session(uint64_t uid, ss_statu statu)
  {
    std::unique_lock<std::mutex> lock(_mutex);
    session_ptr ptr(new session(next_ssid));
    ptr->set_status(statu);
    ptr->set_user(uid);
    _session.insert(std::make_pair(next_ssid, ptr));
    next_ssid++; // 别忘了计数器++
    return ptr;
  } // 使用客户id和状态建立session

  session_ptr get_session_by_ssid(uint64_t ssid)
  {
    std::unique_lock<std::mutex> lock(_mutex);
    auto ptr = _session.find(ssid);
    if (ptr != _session.end())
    {
      return ptr->second;
    }
    return session_ptr();
  } // 使用ssid获取session指针对象

  void append_session(const session_ptr &ssp)
  {
    std::unique_lock<std::mutex> lock(_mutex);
    _session.insert(std::make_pair(ssp->ssid(), ssp));
  }

  void set_session_expire_time(uint64_t ssid, int ms)//ms是要执行的等待时间！！！！！
  {
    // 依赖于websocketpp的定时器来完成session生命周期的管理。
    // 登录之后，创建session，session需要在指定时间无通信后删除
    // 但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
    // 等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
    session_ptr ssp = get_session_by_ssid(ssid);//先通过ssid获取session指针
    if (ssp.get() == nullptr)
    {
      return;
    }
    wsserver_t::timer_ptr tp = ssp->get_timer();//获取session类里面关联的计时器！！！！！！
    if (tp.get() == nullptr && ms == SESSION_FOREVER)//空就是永久存在
    {
      // 1. 在session永久存在的情况下，设置永久存在
      return;
    }
    else if (tp.get() == nullptr && ms != SESSION_FOREVER)//不是forever，那么就有限时任务！！！！！！
    {
      // 2. 在session永久存在的情况下，设置指定时间之后被删除的定时任务
      //做处理时使用server服务器类的里面的计时器函数要使用set_timer进行更新！！！！！ 也要同步到session类中我们关联的计时器！！！！！！！

      wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,std::bind(&session_manager::remove_session, this, ssid));//这里要因为在类外面，所以默认的this指针是要显示传递的，
      ssp->set_timer(tmp_tp);//更新到session关联的计时器中
    }
    else if (tp.get() != nullptr && ms == SESSION_FOREVER)
    {
      // 3. 在session设置了定时删除的情况下，将session设置为永久存在
      // 删除定时任务--- stready_timer删除定时任务会导致任务直接被执行！！！！！
      tp->cancel(); // 因为这个取消定时任务并不是立即取消的，所以后序添加键值对不能直接使用insert，而是要以定时器任务的形式进行添加！！！！------_server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
      // 因此重新给session管理器中，添加一个session信息, 且添加的时候需要使用定时器，而不是立即添加
      ssp->set_timer(wsserver_t::timer_ptr()); // 将session关联的定时器设置为空----因为要求是永久的，所以要设置为空指针！！！！！
      _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));//设置为0，代表要立刻执行！！！
    }
    else if (tp.get() != nullptr && ms != SESSION_FOREVER)
    {
      // 4. 在session设置了定时删除的情况下，将session重置删除时间。
      tp->cancel(); // 因为这个取消定时任务并不是立即取消的
      ssp->set_timer(wsserver_t::timer_ptr());
      _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));

      // 重新给session添加定时销毁任务
      wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,std::bind(&session_manager::remove_session, this, ssp->ssid()));
      // 重新设置session关联的定时器
      ssp->set_timer(tmp_tp);
    }
  }
  // 设置session过期时长！！！！！

  void remove_session(uint64_t ssid)
  {
    std::unique_lock<std::mutex> lock(_mutex);
    _session.erase(ssid);
  }
  // 通过session的id来删除session指针！！！！！
};

#endif